Technical review: Document web app manifest localization#44609
Technical review: Document web app manifest localization#44609chrisdavidmills wants to merge 11 commits into
Conversation
|
Preview URLs (4 pages)
External URLs (4)URL:
URL:
(comment last updated: 2026-07-09 16:21:28) |
|
|
||
| in this example, the app's default `lang` is `en-US`, and its default `dir` is `ltr`. Its default `name` is "Color picker", but we've also specified localized variants in the `name_localized` member. Users that have their primary language preference set to German (`de`), French (`fr`), or Arabic (`ar`) will see the app's name displayed in a more suitable language for them. | ||
|
|
||
| The French variant is specified as French Canadian (`fr-CA`) in its `lang` property, with a direction (`dir`) of `ltr`, while the Arabic variant has its `dir` specified as `rtl`. The German variant doesn't need its `lang` or `dir` specified, so its value is a string containing the localized text. |
There was a problem hiding this comment.
This doesn't quite work because the French Canadian version doesn't need its direction set either.
There was a problem hiding this comment.
I've chatted to @kyerebo about the dir and lang members' involvement in manifest localization, and ... they are confusingly not involved at all. I've therefore made quite a few changes to the articles.
In this particular case (and on the dir page), I've cut the example right down, removing all this stuff.
| "name": "Color Picker", | ||
| "name_localized": { | ||
| "de": "Farbwähler", | ||
| "fr": { "value": "Sélecteur de Couleur", "lang": "fr-CA", "dir": "ltr" }, |
There was a problem hiding this comment.
How does this work - i.e this is fr, but you also have a lang that is "fr-CA". However I know lang sets the default language.
My guess is that this should omit lang and fix up the key.
"fr-CA": { "value": "Sélecteur de Couleur", "dir": "ltr" },
Further, in the intro it says "will see the app's name displayed in a more suitable language for them."
I would like to understand the fallback here. If my language is set to fr, will it choose "fr-CA" or will it choose English ? Can I declare both "fr" and "fr-CA", and so on. The example could show a case or two.
There was a problem hiding this comment.
There was a problem hiding this comment.
Definitely see https://github.com/mdn/content/pull/44609/changes#r3517481036 :-)
There was a problem hiding this comment.
I can't speak to the standards approach to this, but for the Chromium implementation, the localized member's lang field, if specified, doesn't currently have an effect on what the user sees.
The primary criteria for localization selection is if there exists a language tag that matches or partly matches the user's current browser language setting.
So if we had something like:
"fr": { "value": "Color Selector, "lang": "en" },
The end result for a browser set to French is that we'd see Color Selector.
The lang field is effectively acting as a "heads up, this value is this language rather than the one specified by the language tag."
There was a problem hiding this comment.
Again, I've cut this right down. In other places, I've made the text more understandable.
In the particular case of the lang property being different from the main object property key, I've removed the fr-CA example and used a much more obvious one; see the Howto guide's short_name_localized example.
| --- | ||
|
|
||
| The `lang` manifest member is used to specify a default language for your web application, which will be used unless overriden by a different `lang` value found in a [`*_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized) member. | ||
|
|
There was a problem hiding this comment.
Is there a default lang assumed?
There was a problem hiding this comment.
I think including the manifest-level dir and lang as part of this conversation can be confusing.
In the Chromium implementation, the language selection is independent of the manifest-level lang specification, and is solely based on the user's browser language.
There was a problem hiding this comment.
I've updated this. The chosen language has nothing to do with lang. When localized members are included (e.g. name_localized), the browser chooses the variant that matches the user's browser language setting.
If no variant matches the user's browser language setting, the value in the non-localized member is used instead (e.g. name). I've explained this in the *_localized reference and the Howto.
|
|
||
| in this example, the app's default `lang` is `en-US`, and its default `dir` is `ltr`. Its default `name` is "Color picker", but we've also specified localized variants in the `name_localized` member. Users that have their primary language preference set to German (`de`), French (`fr`), or Arabic (`ar`) will see the app's name displayed in a more suitable language for them. | ||
|
|
||
| The French variant is specified as French Canadian (`fr-CA`) in its `lang` property, with a direction (`dir`) of `ltr`, while the Arabic variant has its `dir` specified as `rtl`. The German variant doesn't need its `lang` or `dir` specified, so its value is a string containing the localized text. |
There was a problem hiding this comment.
Same comments as for the lang case.
Note also that this is a dir topic. I'd show a screen shot or explicitly state at least somewhere that the text will be drawn LTR and show it.
There was a problem hiding this comment.
Again, this is all now cut down and changed around.
| page-type: how-to | ||
| sidebar: pwasidebar | ||
| --- | ||
|
|
There was a problem hiding this comment.
How do I localise the rest of the app? We should at least mention that (or if it can't be done, state that too). I assume perhaps we can specify a key for the default language entry point?
There was a problem hiding this comment.
Localizing the rest of the app is a huge topic and out of scope for this article. How you would go about localizing it depends on what kind of app it is, what stack you've used, etc.
https://github.com/oh-jon-paul/awesome-i18n gives you an idea of what's available.
|
|
||
| ## Set a default language and direction | ||
|
|
||
| The [`lang`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/lang) and [`dir`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/dir) members define a default language and language direction for the app. These will be assumed by the browser if no language and direction more suitable for the user's preferences are found in the [`*_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized) variants. In such cases, the non-prefixed members are used (for example, `name`). |
There was a problem hiding this comment.
-
What if I don't set a default language or dir, what is used?
-
While I understand exactly what you mean, this doesn't feel quite right
These will be assumed by the browser if no language and direction more suitable for the user's preferences are found in the
*_localizedvariants.It's because the languages will be used but it isn't
langanddirthat will be used. What will be used is the other non-localized members. You clarify this in the next sentence, but "These" still grates.
There was a problem hiding this comment.
- At least in the Chromium implementation, if no
diris specified for a localized member, it uses the direction of the browser's currently set language. Localizations are selected based off of the browser's currently set language at all times.
There was a problem hiding this comment.
See previous comments.
| sidebar: pwasidebar | ||
| --- | ||
|
|
||
| The `lang` manifest member is used to specify a default language for your web application, which will be used unless overriden by a different `lang` value found in a [`*_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized) member. |
There was a problem hiding this comment.
I'm nearing the end of my review, and I realize that this first doc that I read in the review is confusing:
overriden by a different
langvalue found in a*_localizedmember.
If it specifies the default language for my web application, what sense does it make to override?
There was a problem hiding this comment.
See previous comments.
| }, | ||
| ``` | ||
|
|
||
| The French (`fr`) `short_name` translation shows typical usage of the object value form being used to specify a `lang` property. In this case, our French audience knows our app by its English brand name — "SuperSausage" — and we want to specify that this should be handled as English rather than French (for example, for the purposes of pronounciation). |
There was a problem hiding this comment.
See https://github.com/mdn/content/pull/44609/changes#r3517481036 - lang in a translation needs additional explanation there. I think I get it now, finally on this the second last doc - this allows you to specify that the value from some other language key should be used for a translation, instead of providing your own, or using the default?
Typo too
| The French (`fr`) `short_name` translation shows typical usage of the object value form being used to specify a `lang` property. In this case, our French audience knows our app by its English brand name — "SuperSausage" — and we want to specify that this should be handled as English rather than French (for example, for the purposes of pronounciation). | |
| The French (`fr`) `short_name` translation shows typical usage of the object value form being used to specify a `lang` property. In this case, our French audience knows our app by its English brand name — "SuperSausage" — and we want to specify that this should be handled as English rather than French (for example, for the purposes of pronunciation). |
There was a problem hiding this comment.
I'm reading it as clarification that although the localized object is under the French language tag, the value of the object is presented in English rather than French.
From the web application manifest spec:
To support multilingual content and ensure optimal display and accessibility, it is possible to specify a different language for a localized text object. This is needed for situations where a term or text needs to be presented in a language different from the user's set locale.
There was a problem hiding this comment.
I've fixed the typo
|
|
||
| ## Finished manifest | ||
|
|
||
| Putting this all together, the complete manifest looks like this: |
There was a problem hiding this comment.
I think you need your shortcuts - I want to know how to select my language version.
There was a problem hiding this comment.
Do you mean you want me to include a shortcuts example?
|
|
||
| Localized text field properties have values equal to objects or strings. | ||
|
|
||
| in this example, the app's default `en-US` language `name` is "Color picker", but we've also specified localized variants in the `name_localized` member. Users that have their primary language preference set to German (`de`), French (`fr`), or Arabic (`ar`) will see the app's name displayed in a more suitable language for them. |
There was a problem hiding this comment.
| in this example, the app's default `en-US` language `name` is "Color picker", but we've also specified localized variants in the `name_localized` member. Users that have their primary language preference set to German (`de`), French (`fr`), or Arabic (`ar`) will see the app's name displayed in a more suitable language for them. | |
| In this example, the app's default `en-US` language `name` is "Color picker", but we've also specified localized variants in the `name_localized` member. Users that have their primary language preference set to German (`de`), French (`fr`), or Arabic (`ar`) will see the app's name displayed in a more suitable language for them. |
There was a problem hiding this comment.
But also see my other comment on lang - this could be better worded.
There was a problem hiding this comment.
this is changed now
|
|
||
| in this example, the app's default `en-US` language `name` is "Color picker", but we've also specified localized variants in the `name_localized` member. Users that have their primary language preference set to German (`de`), French (`fr`), or Arabic (`ar`) will see the app's name displayed in a more suitable language for them. | ||
|
|
||
| The French variant is specified as French Canadian (`fr-CA`) in its `lang` property, with a direction (`dir`) of `ltr`, while the Arabic variant has its `dir` specified as `rtl`. The German variant doesn't need its `lang` or `dir` specified, so its value is a string containing the localized text. |
There was a problem hiding this comment.
It isn't clear why the French version needs a lang and dir and the german version does not - same thing as https://github.com/mdn/content/pull/44609/changes#r3517444725
Somewhere else, perhaps in the how to, you indicate that lange in a localized member takes a language from somewhere else. So here you're saying that for French ou take your language from "fr-CA". But then there are two more questions:
- If you are taking it from fr-CA, where is your fr-CA definition?
- If you are taking it from fr-CA, why do you have a value?
I suspect the answer might be "take it from fr-CA if defined and otherwise fall back to the specified value. But that feels like poor design to me.
There was a problem hiding this comment.
Agreed, neither of these actually need dir or lang specified, those are always optional fields.
The manifest spec is actually pretty loose on what lang for localized members should do beyond "specifying when it should be presented as another language".
At least in the Chromium implementation, for current cases that make localized members user-visible, it seems to ignore the lang field if provided.
There was a problem hiding this comment.
I've changed this for a more obvious example
|
|
||
| The `lang` and `dir` members define a default language and language direction that will be assumed by the browser if no language more suitable for the user's preferences is found in the `name_localized` variants. In such cases, the `name` member is used. | ||
|
|
||
| ### Localized images |
There was a problem hiding this comment.
Is it icons or image or both? If it can only be icons we should say that. If icons and other images are different we should say that. We should also note "somewhere" whether the localised set can have diferrent sets of icons or must match the same sizes
There was a problem hiding this comment.
+1 let's change this to "Localized icons"
The localized set can have different sets of icons; each localization is treated independently as if the other localizations didn't exist.
There was a problem hiding this comment.
I've changed it to "icons"
…ndex.md Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
…dex.md Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
…dex.md Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
…nifest/index.md Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
…nifest/index.md Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
…nifest/index.md Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
…nifest/index.md Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
kyerebo
left a comment
There was a problem hiding this comment.
Mostly comments about clarifying the effect of manifest-level lang and dir, thanks!
| sidebar: pwasidebar | ||
| --- | ||
|
|
||
| [Progressive Web App (PWA)](/en-US/docs/Web/Progressive_web_apps) manifests can be localized using a combination of localizable members, which have the suffix [`_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized), and the [`lang`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/lang) and [`dir`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/dir) members. |
There was a problem hiding this comment.
Touched upon this in email with Chris, reposting here in more detail.
The dir and lang manifest-level members are somewhat independent of the localization feature.
*_localized members are selected based off of the browser's language setting, not off of the other manifest members.
This is definitely a bit confusing in the spec, as it says the manifest-level dir/lang set the default value of localized members, but the section on localized members clarifies that the default representation is the manifest member without *_localized and localizations should be selected based off of the user's language setting.
The manifest-level dir is defined in the spec as applying a default text direction for localized members that are missing a dir field, but the Chromium implementation only prioritizes whatever direction is specified by the localized member.
The manifest-level lang field isn't actually used when calculating which localized value to show.
In practice (or at least how it works right now in Chromium), this means that the manifest-level lang and dir don't have implications for the localized value that ends up getting chosen. Perhaps we can remove or edit the references to these manifest level members to avoid confusing developers?
The suggested instructions for working with localizable manifests on the chrome developer blog don't mention manifest-level dir or lang as any sort of fallback.
There was a problem hiding this comment.
I think I've captured this all in my updates, except maybe this bit:
The manifest-level dir is defined in the spec as applying a default text direction for localized members that are missing a dir field, but the Chromium implementation only prioritizes whatever direction is specified by the localized member.
But I'm not sure if there is really much worth saying.
There was a problem hiding this comment.
I agree it's probably not worth specifying. LGTM.
|
|
||
| The [`lang`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/lang) and [`dir`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/dir) members define a default language and language direction for the app. These will be assumed by the browser if no language and direction more suitable for the user's preferences are found in the [`*_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized) variants. In such cases, the non-prefixed members are used (for example, `name`). | ||
|
|
||
| Set a suitable default language and direction like so: |
There was a problem hiding this comment.
Consider removing/editing this section as part of comment above.
|
|
||
| ## Set a default language and direction | ||
|
|
||
| The [`lang`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/lang) and [`dir`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/dir) members define a default language and language direction for the app. These will be assumed by the browser if no language and direction more suitable for the user's preferences are found in the [`*_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized) variants. In such cases, the non-prefixed members are used (for example, `name`). |
There was a problem hiding this comment.
- At least in the Chromium implementation, if no
diris specified for a localized member, it uses the direction of the browser's currently set language. Localizations are selected based off of the browser's currently set language at all times.
| }, | ||
| ``` | ||
|
|
||
| The French (`fr`) `short_name` translation shows typical usage of the object value form being used to specify a `lang` property. In this case, our French audience knows our app by its English brand name — "SuperSausage" — and we want to specify that this should be handled as English rather than French (for example, for the purposes of pronounciation). |
There was a problem hiding this comment.
I'm reading it as clarification that although the localized object is under the French language tag, the value of the object is presented in English rather than French.
From the web application manifest spec:
To support multilingual content and ensure optimal display and accessibility, it is possible to specify a different language for a localized text object. This is needed for situations where a term or text needs to be presented in a language different from the user's set locale.
| The exact properties contained within each object will be the same as the properties contained within the objects included with the non-localized versions of the members: | ||
|
|
||
| - For `icons_localized`, the objects can have the same properties as the [`icons`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/icons) member: `src`, `sizes`, `type`, and `purpose`. | ||
| - For `shortcuts_localized`, the objects can have the same properties as the [`shortcuts`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/shortcuts) member: `name`, `short_name`, `description`, `url`, and [`icons`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/icons). |
There was a problem hiding this comment.
Maybe it makes sense to break out shortcut localization to its own section. Localization of shortcuts looks a little different than described here. Rather than shortcuts having a corresponding *_localized version, it's that shortcut fields can be localized.
An example from this test site:
"shortcuts": [
{
"name": "Open Dashboard",
"name_localized": {
"en": { "value": "Open Dashboard", "lang": "en", "dir": "ltr" },
"de": { "value": "Dashboard öffnen", "lang": "de", "dir": "ltr" },
"ar": { "value": "فتح لوحة المعلومات", "lang": "ar", "dir": "rtl" }
},
"short_name": "Dashboard",
"short_name_localized": {
"en": { "value": "Dashboard", "lang": "en", "dir": "ltr" },
"de": { "value": "Dashboard", "lang": "de", "dir": "ltr" },
"ar": { "value": "لوحة", "lang": "ar", "dir": "rtl" }
},
"description": "Go to your dashboard.",
"description_localized": {
"en": { "value": "Go to your dashboard.", "lang": "en", "dir": "ltr" },
"de": { "value": "Zum Dashboard wechseln.", "lang": "de", "dir": "ltr" },
"ar": { "value": "انتقل إلى لوحتك.", "lang": "ar", "dir": "rtl" }
},
"url": "./dashboard",
"icons": [
{ "src": "./icons/shortcut-dashboard.png", "sizes": "96x96", "type": "image/png", "purpose": "any" }
],
"icons_localized": {
"en": [
{ "src": "./icons/icon-128.png", "sizes": "128x128", "type": "image/png", "purpose": "any" }
],
"de": [
{ "src": "./icons/localized_icons/de/Iconka-Meow-Cat-purr.128.png", "sizes": "128x128", "type": "image/png", "purpose": "any" }
],
"ar": [
{ "src": "./icons/localized_icons/ar/black_cat-128.png", "sizes": "128x128", "type": "image/png", "purpose": "any" }
]
}
}
],
There was a problem hiding this comment.
OK, I think I've made all the updates I need, except for more coverage of shortcuts. I'll work on that tomorrow.
|
|
||
| in this example, the app's default `en-US` language `name` is "Color picker", but we've also specified localized variants in the `name_localized` member. Users that have their primary language preference set to German (`de`), French (`fr`), or Arabic (`ar`) will see the app's name displayed in a more suitable language for them. | ||
|
|
||
| The French variant is specified as French Canadian (`fr-CA`) in its `lang` property, with a direction (`dir`) of `ltr`, while the Arabic variant has its `dir` specified as `rtl`. The German variant doesn't need its `lang` or `dir` specified, so its value is a string containing the localized text. |
There was a problem hiding this comment.
Agreed, neither of these actually need dir or lang specified, those are always optional fields.
The manifest spec is actually pretty loose on what lang for localized members should do beyond "specifying when it should be presented as another language".
At least in the Chromium implementation, for current cases that make localized members user-visible, it seems to ignore the lang field if provided.
|
|
||
| The `lang` and `dir` members define a default language and language direction that will be assumed by the browser if no language more suitable for the user's preferences is found in the `name_localized` variants. In such cases, the `name` member is used. | ||
|
|
||
| ### Localized images |
There was a problem hiding this comment.
+1 let's change this to "Localized icons"
The localized set can have different sets of icons; each localization is treated independently as if the other localizations didn't exist.
| --- | ||
|
|
||
| The `dir` manifest member is used to specify a default language directionality for your web application, which will be used unless overriden by a different `dir` value found in a [`*_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized) member. | ||
| If not specifies, the default direction is inferred from the default language, as specfiied using [`lang`]. |
There was a problem hiding this comment.
This is all changed now.
| --- | ||
|
|
||
| The `lang` manifest member is used to specify a default language for your web application, which will be used unless overriden by a different `lang` value found in a [`*_localized`](/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/*_localized) member. | ||
|
|
There was a problem hiding this comment.
I think including the manifest-level dir and lang as part of this conversation can be confusing.
In the Chromium implementation, the language selection is independent of the manifest-level lang specification, and is solely based on the user's browser language.
| "name": "Color Picker", | ||
| "name_localized": { | ||
| "de": "Farbwähler", | ||
| "fr": { "value": "Sélecteur de Couleur", "lang": "fr-CA", "dir": "ltr" }, |
There was a problem hiding this comment.
I can't speak to the standards approach to this, but for the Chromium implementation, the localized member's lang field, if specified, doesn't currently have an effect on what the user sees.
The primary criteria for localization selection is if there exists a language tag that matches or partly matches the user's current browser language setting.
So if we had something like:
"fr": { "value": "Color Selector, "lang": "en" },
The end result for a browser set to French is that we'd see Color Selector.
The lang field is effectively acting as a "heads up, this value is this language rather than the one specified by the language tag."
|
Thanks for making those changes, the new technical descriptions LGTM. |
Description
Chrome and Edge 148 desktop have added support for
*_localizedmembers, aka localizable web app manifests. See https://chromestatus.com/feature/5090807862394880.This PR adds reference documentation for
*_localizedmembers and thedirandlangmembers, as well as a how-to guide covering how to localize a web app manifest.Motivation
Additional details
Related issues and pull requests