krakenjs / makara Goto Github PK
View Code? Open in Web Editor NEWAn internationalization module for kraken and express
License: Other
An internationalization module for kraken and express
License: Other
Looks like currently it is not possible to fallback to default locale for single keys, it would be nice to have this important feature.
Let say we have 2 locales with fallback to US_en:
US/en/layout/master.properties and GB/en/layout/master.properties
in US/en/layout/master.properties:
key1=value
key2=value
key3=value
key4=value
and for GB layout we want to overwrite only few keys , so in file GB/en/layout/master.properties:
key3=new value
key4=new value
in GB layout template, all keys that don't exist in GB's master.properties file render as key names: ☃key1☃ and ☃key2☃ with no values.
It would be very nice if those keys values can be populated automatically from fallback locale bundle so we don't have to add/repeat them in all other locales.
We use makara with kraken-js.
Would be good to have a scheduled rollout of makara deprecation possibly including:
I think we can eliminate the feature entirely: It's used seldom and when it's used, it's mostly abused. Quoting takes more contextual awareness than a dust helper can give.
so, let's say that you support GB (UK) but you only support one language (en) but for some reason a user has french in the accept-language header and country=GB, so i don't want it to fallback to fr-FR, i want it to fallback to en-GB, is that possible?
Hi, I have spent several hours trying to set enableMetadata parameter to true but I can't find how to do it.
I have tried several configuration options without success like:
"middleware": { "makara": { "priority": 100, "enabled": true, "module": { "name": "makara", "arguments": [ { "i18n": "config:i18n", "specialization": "config:specialization" "enableMetadata": "true" } ] } },
...
While debugging, I have the options parameter in dust-message-helper as undefined.
What can I do?
I recently upgraded a Kraken project to Makara 2, and I discovered that the combination of mode="paired"
and @provide
does not work anymore.
I took me a while to figure out that @provide
(from dustmotes-provide) is not able to consume the output of @message
. I think that this breaking change should be mentioned somewhere (at least in this page).
If anyone is interested, I solved the problem by writing my own @provide
helper: dust-provide-helper. It is not perfect, but it can be used as a drop-in replacement for dustmotes-provide with Makara 2.
Hello
actually npm audit gives this
This is because in the package.json you use bundalo 0.2.9 which is dependent of a lodash vesion which is unsafe.
Could you at least upgrade to 1.0.0 since lodash seems to has been removed from this version.
Actually applications using makara (or krakenjs) are flagged insecure by npm audit...
Looks like Grunt expands patterns like **/*.dust
and **\\*.dust
with no regard to the host OS path separator. It's always a /
Under Windows, this causes issues with the following code:
Proper solution: Find a way for the globbing to respect the OS separator.
Very ugly workaround 💩 : Assume that everything works with /
. Don't like the brittleness of this and would only use it as a last resort.
Hi, I've followed http://krakenjs.com/2015/07/09/upgrading-makara.html and upgraded my v1.0 app running on Windows. Now the specialization rules don't seem to be processed any more, I've verified the values are still being set, but the correct specialized partials no longer show, only the default one does. Any ideas? Let me know if you need any snippets. Thanks.
I’ve encountered a common localization issue that I’ve been unable to solve with Makara and Dust.
Issues I’m trying to address:
The first two are both attempts to DRY things up as well as keep markup in the domain of front end engineers and out of the hands of localization editors. The third is an attempt to not require trailing whitespace in property values, e.g., common.grammar.comma=,
(Makara; note the trailing whitespace after the comma) versus common.grammar.comma={0}, {1}
(Java).
I don’t see a way to accomplish this without splitting the disclaimer into multiple properties then stitching them back together. Note that this would require saving trailing whitespace in the properties files for some locales (unfortunate since most IDEs will strip it). Also, this solution won’t work with all strings, as the order of the “pieces” sometimes changes from locale to locale (especially with CJK).
payment.disclaimer.a=By clicking “
payment.submit=Buy Now
# Note the trailing whitespace
payment.disclaimer.b=,” you agree to the
payment.disclaimer.c=Terms of Sale
# Note the leading and trailing whitespace
payment.disclaimer.d= and
payment.disclaimer.e=Privacy Policy
payment.disclaimer.f=.
Is what I want to accomplish even possible? Do I just need to write a custom helper?
To clarify what I’m asking, here’s a real-world example using Java and FreeMarker:
<!-- payment.ftl -->
<#assign tosLink>
<a href="/${locale}/termsofsale" tabindex="1">${msg("payment.disclaimer.tosLink")}</a>
</#assign>
<#assign privacyLink>
<a href="/${locale}/termsofsale" tabindex="1">${msg("payment.disclaimer.privacyLink")}</a>
</#assign>
<p>${msg("payment.disclaimer.text", [msg("payment.submit"), tosLink, privacyLink], false)}</p>
<button class="btn btn-primary btn-wide" id="payment-submit" tabindex="1">${msg("payment.submit")}/button>
# payment_en_US.properties
payment.disclaimer.text=By clicking “{0},” you agree to the {1} and {2}.
payment.disclaimer.tosLink=Terms of Sale
payment.disclaimer.privacyLink=Privacy Policy
payment.submit=Pay Now
# payment_en_GB.properties
payment.disclaimer.text=By clicking ‘{0}’, you agree to the {1} and {2}.
payment.disclaimer.tosLink=Terms of Sale
payment.disclaimer.privacyLink=Privacy Policy
payment.submit=Pay Now
# payment_fr_FR.properties
payment.disclaimer.text=En cliquant sur « {0} », vous acceptez les {1} et la {2}.
payment.disclaimer.tosLink=Conditions de Vente
payment.disclaimer.privacyLink=Politique de Confidentialité
payment.submit=Payer
# payment_ko_KR.properties
payment.disclaimer.text=“{0}”를 눌러 {1},{2}에 동의합니다.
payment.disclaimer.tosLink=대한민국 거주자에 대한 판매 조건
payment.disclaimer.privacyLink=개인정보 취급방침
payment.submit=구매하기
# payment_zh_TW.properties
payment.disclaimer.text=點擊「{0}」代表您同意{1}與{2}。
payment.disclaimer.tosLink=銷售協議
payment.disclaimer.privacyLink=隱私權政策
payment.submit=現在付款
Desired output:
<!-- en_US -->
<p>By clicking “Pay Now,” you agree to the <a href="/en-us/termsofsale" tabindex="1">Terms of Sale</a> and <a href="/en-us/privacy" tabindex="1">Privacy Policy</a>.</p>
<button class="btn btn-primary btn-wide" id="payment-submit" tabindex="1">Pay Now</button>
<!-- en_GB -->
<p>By clicking ‘Pay Now’, you agree to the <a href="/en-gb/termsofsale" tabindex="1">Terms of Sale</a> and <a href="/en-gb/privacy" tabindex="1">Privacy Policy</a>.</p>
<button class="btn btn-primary btn-wide" id="payment-submit" tabindex="1">Pay Now</button>
<!-- fr_FR -->
<p>En cliquant sur « Payer », vous acceptez les <a href="/fr-fr/termsofsale" tabindex="1">Conditions de Vente</a> et la <a href="/fr-fr/privacy" tabindex="1">Politique de confidentialité</a>.</p>
<button class="btn btn-primary btn-wide" id="payment-submit" tabindex="1">Payer</button>
<!-- ko_KR -->
<p>“구매하기”를 눌러 <a href="/ko-kr/termsofsale" tabindex="1">대한민국 거주자에 대한 판매 조건</a>,<a href="/ko-kr/privacy" tabindex="1">개인정보 취급방침</a>에 동의합니다.</p>
<button class="btn btn-primary btn-wide" id="payment-submit" tabindex="1">구매하기</button>
<!-- zh_TW -->
<p>點擊「現在付款」代表您同意<a href="/zh-tw/termsofsale" tabindex="1">銷售協議</a>與<a href="/zh-tw/privacy" tabindex="1">隱私權政策</a>。</p>
In dev mode, makara sees the i18n config specifying enableMetadata and functions correctly. However, when the grunt build scripts run and pre-compile the templates, they do not take the config into consideration so all dust templates lack the metadata desired
in README, it says: There's nothing inherently dust-centric about makara, but it does provide dust template engines as a default.
So, dustjs template engine will get installed even if i am using react.
The grunt-makara-amdify 1.0.1 uses the makara-builder 1.0.0 and in makara-builder/index.js I can see:
var locales = paths.map(function (p) {
var m = /(.*)\/(.*)/.exec(path.relative(localeRoot, p));
return m[2] + '-' + m[1];
});
localeRoot="...\locales" and p="...locales/DE/de"
The path.relative() call transforms p into windows style and returns "DE\de".
The reg ex evaluation returns null because of the hardcoded linux path sep.
The following line gives of course "Fatal error: Cannot read property '2' of null".
Any hint for a workaround? Beside "use linux" ... ;-)
In Dust, we can do this to make a dynamic partial reference:
{>"flowViews/flowView{flowName}" /}
But sadly, we can't do this:
{@pre type="content" key="myprefix{countryCode}.mykey" /}
It would be more native-like behavior to allow content keys to be dynamic and allow us a way of avoiding logical structures in the Dust code.
In the "upgrading makara" guide, at the end it's written:
Adjust your client accordingly, and use dust-usecontent-helper and dust-message-helper or dust-intl to load the content for your templates.
But the README of these doesn't tell us how to use them in the browser so I'm a bit lost. Either these READMEs should be updated with client side examples or the upgrading makara guide should be a little bit more detailed (ideally both).
In the meantime, I'll continue investigating so maybe this issue will be transformed in a pull request in the helpers repo.
https://github.com/krakenjs/makara/blob/v2.x/index.js#L67
On an middleware i set req.locale = 'es-NI'; // or en-US but don't change.
Any idea?
Thanks
JSON syntax (which isn't so relevant, since anything that has strings and nested arrays and objects can support the same semantics) :
login.json
{
"login": {
"username": "Username",
"brandname": { "value": "Brand Name(tm)", "do_not_translate": true }
},
"includes": [
"form.json"
]
}
form.json
{
"submit": "Submit",
"error": "there was a error!"
}
Includes could be used as a fallback mechanism, too, explicitly forming a relationship between resource files, rather than any implicit or higher level relationship between language files.
CSON:
"login":
"username": "Username"
"brandname":
"value": "Brand Name(tm)"
"do_not_translate": true
"includes": [
"form.json"
]
YAML:
"login":
"username": "Username"
"brandname":
"value": "Brand Name(tm)"
"do_not_translate": true
"includes":
- form.json
Properties with comment annotations:
login.username=Username
login.brandname=Brand Name(tm) # @do_not_translate=true
include=form.json
I read in #21 that you cannot pass in content key dynamically to a template. I am curious whether my issue in #35 could be solved by having a list of keys in the model object and the template could evaluate those keys and inject the proper content from the current resource bundle.
Example Model:
...
filters: [
{name_key:'filter.id', hint_key:'filter.hint.id'},
{name_key:'filter.name', hint_key:'filter.hint.name'},
{name_key:'filter.title', hint_key:'filter.hint.title'},
{name_key:'filter.email', hint_key:'filter.hint.email'},
...
],
...
Example property bundle:
filter.id=ID
filter.hint.id=E.g. '1234'
filter.name=Name
filter.hint.name=E.g. 'Mike' or 'Sally Jones'
filter.title=Title
filter.hint.title=E.g. 'Broker'
filter.email=Email
filter.hint.email=E.g. '[email protected]' or 'someco.com'
On the template perhaps:
...
{#filters}
<a href='#' title='{@pre type="content" key="{hint_key}" /}'>{@pre type="content" key="{name_key}" /}</a>
{/filters}
...
I appreciate steer on how best to accomplish #35 and whether this might be possible but I'm missing something.
it is possible to put a map in a list ?
in my properties file :
index.map[0].aa=foo
index.map[1].aa=bar
In this case the bundle of index return this :
{ map: [ 'foo', 'bar ] }
But would like something like that :
{ map: [{aa : 'foo'},{aa: 'bar'}]}
It is possible ?
I use makara with kraken-js.
Thanks !
When deploying a build to heroku, the compile/deploy step fails due to a version conflict between my package.json containing the latest (2.3.x) of dustjs-linkedin and the specified 2.0.0 version in Makara. There is a similar problem with the declared version of dustjs-linkedin in the Adaro package.json.
I'd suggest setting them to use 2.x.x instead of explicit versions and only make it more restrictive if it's found LinkedIn changed the dust signature/API.
Our application is using a layout template. If we supply a dynamic partial that has a forward slash in the path, somewhere during the dust rendering the forward slash is being converted to an HTML entity, so we get an error like the following:
Express
500 Error: ENOENT, open '/Users/hdoan/Sites/Source/OurApp/public/templates/someDirectory/anotherDirectory/myPage.dust'
As the title says.
The culprit is in /lib/translator/index.js
at line 41.
template = path.join(templateRoot || this.templateRoot, name + '.dust');
When rending for many concurrent requests, patch/unpatch interleaving could cause inconsistent state causing rendering failure and app crashiness.
This is possibly an error with the dustjs-i18n.js task in makara. It would seem this line would make an invalid RexExp if the path separator is ''
var regexp = new RegExp('([' + path.sep + ']?)$');
I can see the mention of mode="paired", but it's still not very clear on the use of it in dust template context. Can you add an example right under that section in README?
With caching disabled, the following formats are both supported:
res.locals.context = {
locality: 'en_US'
};
res.locals.context = {
locality: {
country: 'US',
language: 'en'
}
};
however with cache enabled only the latter successfully resolves.
On the blog, you have this
"dust": {
"helpers": [
{
"module": "dust-makara-helpers",
"arguments": [ { "autoloadTemplateContent": false } ]
}
]
}
It actually needs to be this
"dust": {
"helpers": [{
"name": "dust-makara-helpers",
"arguments": { "autoloadTemplateContent": false }
}]
},
Either I am missing something or makara seems to be broken. I followed the guide in the read me and set up makara as view engine:
// ...snip
const dustConfig = {
cache: !config.development,
helpers: [
"dustjs-helpers",
"dust-makara-helpers",
debug,
resolve(app),
inline,
adv
]
};
if (app.get("env") === "development") {
app.set("views", path.join(config.root, "app/views"));
app.engine("dust", makara.dust(dustConfig));
app.set("view engine", "dust");
} else {
// use cached, precompiled templates
app.set("views", path.join(config.root, "build/app/views"));
app.engine("js", makara.js(dustConfig));
app.set("view engine", "js");
}
app.use(makara({
i18n: {
contentPath: path.join(config.root, "locales"),
fallback: "de-DE"
}
}));
// ...snip
Folder structure is ./build/app/views/DE/de/kaufberatung.js
and so on.
Looking at the debug output it seems that makara isn't even trying to look into the language folders.
ENGINE-MUNGER 24031: specialization mapped 'kaufberatung.js' to 'kaufberatung.js'
ENGINE-MUNGER 24031: lookup "kaufberatung.js"
ENGINE-MUNGER 24031: stat "~/workspace/project/build/app/views/kaufberatung.js"
ENGINE-MUNGER 24031: stat "~/workspace/project/build/app/views/kaufberatung.js/index.js"
Looking at the code in makara/index.js
it seems like there should be some code to setup i18n for .js
files but all I can find is the .properties
setup.
(x-post from engine-munger)
I'm using makara in a kraken (yo generated) application. Presently looking for some documentation to find the right approach to manually set the i18n of the express.js http response
object. So that right .properties
file gets picked-up based on the business logic.
Below is one method I found by trail and error, where the GB version of properties file gets picked-up correctly even though my browser has Accept-Language
for US locale.
res.render('index', { name: 'index', locale: 'en_GB'});
However, not certain if this is the right way, or could there be some situation that I'm unaware of during the run time where (user/browser settings, or, ..?) the above approach will get overridden and an incorrect .properties
file will get picked up?
During ajax call, the messages are set in nodejs, so we want to read resource bundle on nodejs?
Hi, I'm following the i18n example and makara 2.0 blog post / docs. but I'm not able to override the fallback locale in my kraken. Probably I'm missing something.
I have my middleware to read locale from cookies and setting it in the res.locals.locale and req.locale .
'use strict';
var bcp47 = require('bcp47');
module.exports = function () {
return function (req, res, next) {
var locale = req.cookies && req.cookies.locale;
//Set the locality for this response. The template will pick the appropriate bundle
res.locals.locale = req.locale = bcp47.parse(locale);
//res.locals.locale = req.locale = locale //I have tried also this
next();
};
};
And in my config.json I have:
"locale": {
"priority": 95,
"enabled": true,
"module": {
"name": "path:./lib/locale"
}
}
I have declared a route to set locale cookie into my index.js controller (usage eg: /setLanguage/EN-us )
router.get('/setLocale/:locale', function (req, res) {
res.cookie('locale', req.params.locale);
res.redirect('/');
});
The middleware works fine, and sets res.locals.locale and req.locale to the bcp47 object according to the locale cookie value.
But on the template rendering I always get the default locale message, and not my localized message taken from the correct .properties files.
I'm missing something or does it looks correct?
From the docs:
"If you have content you want to share across pages, then you should factor out use of that content into a separate partial and use that partial to achieve content sharing."
Can you please explain this a bit ?
For example, if I have messages and want them to be available to every template, how do I do that ?
This line basically says if (!filename) console.log("Couldn't find file", filename)
which inevitably says "Couldn't find file undefined", which isn't super helpful. I'm working through an issue with this. Once I figure out why it's not defined I'll try to submit a pull request that more accurately describes the circumstances.
https://github.com/paypal/makara/blob/master/lib/provider/bundle.js#L63
It seems that makara does something strange that causes an express app to become unresponsive if multiple partial includes are used.
A sample app to illustrate this behaviour can be found in my github.
makara-bug
As far as I can tell this doesn't relate to adaro since the partials get resolved correctly. However the render function never finishes and the app just hangs without throwing any error.
I see in #33 using the paired mode you can pass the JSON into the custom helper. I am curious how best to accomplish something without a select list, but dynamically displaying a list of anchor tags with tooltip.
Desired:
<span class="some-list">
<a href="#" title="l10n hint [0]">l10n label [0]</a>
<a href="#" title="l10n hint [1]">l10 lable [1]</a>
...
</span>
Properties:
filter.heading=Filter people by
peopleFilter[0]=ID
peopleFilterHint[0]=E.g. '1234'
peopleFilter[1]=Name
peopleFilterHint[1]=E.g. 'Mike' or 'Sally Jones'
peopleFilter[2]=Title
peopleFilterHint[2]=E.g. 'Broker'
peopleFilter[3]=Email
peopleFilterHint[3]=E.g. '[email protected]' or 'someco.com'
Ideally the template will iterate through the list of field names and display them as anchors but the tooltip will also be localized.
I tried the following that works only for anchors without paired
mode, but cannot seem to get paired
mode to work as in the #33 example.
Worked:
{@pre type="content" before="<a href='/'>" key="peopleFilter" after="</a>" sep=" " /}
Failed (still unsure how to reference 2 keys with same index?):
{@provide selected=chosen}
{#peopleFilter}
<a href="/">{$elt}</a>
{/peopleFilter}
{:peopleFilter}
{@pre type="content" key="peopleFilter" mode="paired" /}
{/provide}
I guess one workaround would be to create a list of the full HTML anchor tags with hints but I figure there has to be a way to accomplish this. Suggestions appreciated and I figure this might be helpful to others as well.
Hi,
Can someone show me how to use bundle.get() with variables? I tried the following to no avail, and I was unable to find any examples which use them.
bundle.get('login.success_message', { username: 'Nathan' })
Does such functionality exist? The above prints "You are now logged in as {username}."
Thanks.
I know the docs says this:
"If you have content you want to share across pages, then you should factor out use of that content into a separate partial and use that partial to achieve content sharing."
But I don't believe this makes any sense. As an example, I simply would like the word 'login' to use a common.login
key. Surely it would be breaking the principles of DRY to replicate this key based on the location in which you are using it and likewise it is overkill to create a partial for one individual key.
I was wondering if this limitation is ever likely to change or if you have good reason for it that I am unaware of.
Thanks!
Matt
Let's say I would like to make a separate template file per locale in a Kraken application. Something like this:
/templates
/US -??
myview.dust
/GB -??
myview.dust
And properties structure like
/US
/en
myview.properties
/GB
/en
myview.properties
Do I need to specify a custom Makara formatPath
function, or should there be some other approach?
What would be a good approach to give the user an option to set their language using this module ?
I thought about storing the language information in a cookie and then inject it into the response on each request using a middleware but is there a better approach ?
Hi,
I have problem with convert a locale to a path fragment if the locale has only 2 chars (ex. cs (Czech)). It can't find the properties files in path ./locales/cs/index.properties (throw error Failed to lookup view). The path is correct.
With locale en_US everything works great.
Is this a bug?
So I would like to change i18n.formatPath but I can't to figure out how to do this in krakenjs. Can you help me, please?
our app is completely react based and we don't want to bring any dustjs related dependencies.
The enableMetadata: true
option allows to weave edit tags around content in a page. Is there some way by which I can retain content in properties files and still have makara not weave edit tags for few elements?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.